package com.bj58.mis.apm.client.boot.http;

import com.bj58.mis.apm.client.boot.APMClient;
import com.bj58.mis.apm.client.core.util.JsonUtil;
import com.bj58.mis.apm.client.core.util.MapUtil;
import com.bj58.mis.apm.collector.http.HttpAccessLog;
import com.bj58.mis.apm.collector.http.HttpAccessStatistic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @author: zhangfx
 * @create: 2018-06-08 16:17
 **/
public class HttpRecordUtil {

    private static Logger logger = LoggerFactory.getLogger(HttpRecordUtil.class);

    public static void recordHttpAccessLog(HttpServletRequest request, HttpServletResponse response, long startTime, Throwable e){
        String requestPath = request.getServletPath();
        if("/favicon.ico".equals(requestPath)){
            return;
        }

        HttpAccessLog accessLog = buildHttpAccessLog(request, response, startTime, e);
        APMClient.collect(accessLog);

        HttpAccessStatistic httpStatistic = buildHttpStatistic(accessLog);
        APMClient.collect(httpStatistic);

        if(logger.isDebugEnabled()){
            Map<String, Object> info = buildHttpAccessMap(request, response, startTime, e);
            String accessInfo = "[HttpAccess] ["+accessLog.getRequestMethod()+"] requestPath = "+accessLog.getRequestPath()+", 耗时: "+accessLog.getTimeTaken()+" ms, status="+accessLog.getResponseStatus();
            logger.debug(accessInfo + "\n" + MapUtil.mapToPrintString(info, ""));
            //logger.debug(accessInfo);
        }

    }

    private static HttpAccessStatistic buildHttpStatistic(HttpAccessLog accessLog) {
        HttpAccessStatistic statistic = new HttpAccessStatistic();
        statistic.setRequestPath(accessLog.getRequestPath());
        statistic.setRequestMethod(accessLog.getRequestMethod());
        statistic.setRequestTime(accessLog.getRequestTime());
        statistic.setResponseStatus(accessLog.getResponseStatus());
        statistic.setRequestCount(1);
        statistic.setMaxTimeTaken(accessLog.getTimeTaken());
        statistic.setMinTimeTaken(accessLog.getTimeTaken());
        statistic.setAvgTimeTaken(accessLog.getTimeTaken());
        statistic.setTotalTimeTaken(accessLog.getTimeTaken());
        return statistic;

    }

    private static HttpAccessLog buildHttpAccessLog(HttpServletRequest request, HttpServletResponse response, long startTime, Throwable e){
        int timeTaken = (int) (System.currentTimeMillis() - startTime);
        HttpAccessLog accessLog = new HttpAccessLog();
        accessLog.setRequestPath(request.getServletPath());
        accessLog.setRequestMethod(request.getMethod());
        accessLog.setRemoteAddr(request.getRemoteAddr());
        accessLog.setParameters(JsonUtil.toJson(getParameterMap(request)));
        accessLog.setRequestTime(startTime);
        accessLog.setRequestHeaders(JsonUtil.toJson(getRequestHeaderMap(request)));
        accessLog.setResponseStatus(response.getStatus());

        accessLog.setResponseHeaders(JsonUtil.toJson(getResponseHeaderMap(response)));
        accessLog.setResponseContentType(response.getContentType());
        accessLog.setResponseContentLength(getResponeContentLength(response));

        accessLog.setTimeTaken(timeTaken);
        if(e!=null){
            accessLog.setException(e.toString());
        }
        return accessLog;
    }

    private static Map<String, Object> buildHttpAccessMap(HttpServletRequest request, HttpServletResponse response, long startTime, Throwable e){
        int timeTaken = (int) (System.currentTimeMillis() - startTime);
        String requestPath = request.getServletPath();
        Map<String, Object> info = new LinkedHashMap<>();
        info.put("requestPath", requestPath);
        info.put("requestMethod", request.getMethod());
        info.put("remoteAddr", request.getRemoteAddr());
        info.put("parameters", getParameterMap(request));
        info.put("requestTime", new Date(startTime));
        info.put("requestHeaders", getRequestHeaderMap(request));
        info.put("responseStatus", response.getStatus());
        info.put("responseHeaders", getResponseHeaderMap(response));
        info.put("responseContentType", response.getContentType());
        info.put("responseContentLength", getResponeContentLength(response));
        info.put("timeTaken", timeTaken);
        if(e!=null){
            info.put("exception", e);
        }
        return info;
    }

    private static Long getResponeContentLength(HttpServletResponse response){
        String contentLength = response.getHeader("Content-Length");
        if(contentLength!=null && !contentLength.isEmpty()){
            return Long.parseLong(contentLength);
        }
        return null;
    }



    private static Map<String, Object> getRequestHeaderMap(HttpServletRequest request){
        Map<String, Object> headerMap = new HashMap<>();
        Enumeration<String> headerEnum = request.getHeaderNames();
        while(headerEnum.hasMoreElements()){
            String headerName = headerEnum.nextElement();
            headerMap.put(headerName, request.getHeader(headerName));
        }
        return headerMap;
    }
    private static Map<String, Object> getResponseHeaderMap(HttpServletResponse response){
        Map<String, Object> headerMap = new HashMap<>();
        for(String headerName : response.getHeaderNames()){
            headerMap.put(headerName, response.getHeader(headerName));
        }
        return headerMap;
    }
    private static Map<String, Object> getParameterMap(HttpServletRequest request){
        Map<String, Object> parameterMap = new HashMap<>();
        for(Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()){
            String[] value = entry.getValue();
            if(value==null || value.length==0){
                parameterMap.put(entry.getKey(), null);
            }else {
                parameterMap.put(entry.getKey(), value[0]);
            }
        }
        return parameterMap;
    }
}
